const Service = require('egg').Service;

const moment = require('moment');
const xml2js = require('xml2js');
const crypto = require('crypto');
const fs = require('fs');
const urlencode = require('urlencode');
const urldecode = require('urldecode');
// service中不知道怎样直接调用helper
const helper = require('../extend/helper.js');

class FuyouService extends Service {
	
	// 创建订单
	async createOrder(requestObj, customInfo) {
		
		const ctx = this.ctx;
		
		//addn_inf=订单描述&curr_type=CNY&goods_des=测试订单&goods_detail=&goods_tag=&ins_cd=08M0026945&limit_pay=&mchnt_cd=0006510F2756892
//		&mchnt_order_no=143205020191009160148&notify_url=http://api.bapushop.com/notify/fynotice&openid=&order_amt=1&product_id=
//&random_str=1570608110&sub_appid=&sub_openid=op3ezjs6t5ZGCnb7M3U1699S08-8&term_id=88888888&term_ip=127.0.0.1&
//trade_type=JSAPI&txn_begin_ts=20191009160150&version=1
//		var reqJson = this.app.config.fuyou.parameters;
		var reqJson = {};
		var pcPayConfig = customInfo.pcPayConfig;
//		pcPayConfig = JSON.parse(pcPayConfig);
		
		reqJson.ins_cd = this.app.config.fuyou.parameters.ins_cd;
		reqJson.mchnt_cd = pcPayConfig.fuyou.mchnt_cd;
		reqJson.goods_des = requestObj.orderDesc;
		reqJson.order_amt = requestObj.payMoney;
		reqJson.notify_url = ctx.request.origin + '/tkPayCallback/fy';
		reqJson.product_id = "";
		reqJson.addn_inf = "";
		reqJson.curr_type = "CNY";
		reqJson.term_id = this.app.config.fuyou.parameters.term_id;
		reqJson.goods_detail = "";
		reqJson.goods_tag = "";
		reqJson.version = this.app.config.fuyou.parameters.version;
		reqJson.random_str = helper.getUuid();
		// 订单号前缀固定为1432
		reqJson.mchnt_order_no = "1432FY" + moment().utc().utcOffset(8).format("YYYYMMDDHHmmss") + "XD" + Math.ceil(Math.random()*10000);
		reqJson.term_ip = requestObj.clientIp;
		reqJson.txn_begin_ts = moment().utc().utcOffset(8).format("YYYYMMDDHHmmss");
		reqJson.limit_pay = "";
		reqJson.trade_type = requestObj.payChannel;  //订单类型:JSAPI--公众号支付、FWC--支付宝服务窗、LETPAY-小程序、BESTPAY--翼支付js
		reqJson.openid = '';
		reqJson.sub_openid = requestObj.openid;
		reqJson.sub_appid = ''; // 巴谱

		console.log(reqJson);
		var sign = await this.createSign(reqJson);

		reqJson.sign = sign;

		var reqXml = await this.turnReqXml(reqJson);
		// 下单地址
		var reqUrl = this.app.config.fuyou.reqHost + this.app.config.fuyou.apiOrderCreateUrl;
		reqUrl = reqUrl.replace(/ /, "+");

		// 只用一次gbk，其它方式urlencode结果都不对
		reqXml = urlencode(urlencode(reqXml, 'gbk'));

		var reqData = "req=" + reqXml;
		console.log(reqData)
		// 记录请求日志
		const logId = await ctx.service.common.addReqLog('FY_createOrder', reqUrl, reqData);
		const result = await this.app.curl(reqUrl, {
			// 必须指定 method
			method: 'POST',
//			// 明确告诉 HttpClient 以 JSON 格式处理返回的响应 body
			dataType: 'text',
//			// 直接发送原始 xml 数据，不需要 HttpClient 做特殊处理
			content: reqData,
			timeout: 9000,
			headers: {
				'content-type': 'application/x-www-form-urlencoded',
			},
		});
		// 记录返回日志
		ctx.service.common.setReqLog(logId, JSON.stringify(result));
		console.log('reqUrl:' + reqUrl);
		console.log(result);

		if (result.status != 200){
			return result.res.statusMessage;
		}
//		console.log(urlencode.decode(result.data, 'gb2312'));
		console.log(result.data);
		var resultData = urlencode.decode(result.data, 'gb2312');
		resultData = resultData.replace('<?xml+version="1.0"+encoding="GBK"+standalone="yes"?>', '<?xml version="1.0" encoding="GBK" standalone="yes"?>');

		var xml = resultData;
		const fxp = require("fast-xml-parser");
		const xml2json = fxp.parse(xml);
		console.log('返回的结果转JSON:');
		console.log(xml2json);
		
		xml2json.xml.mchnt_order_no = reqJson.mchnt_order_no;
		return xml2json.xml;
	}
	
	// 处理回调
	async payCallback(){
		
		const ctx = this.ctx;
		// 获取post参数
		const requestObj = ctx.request.body;

		var resultXml = urlencode.decode(requestObj.req, 'gb2312');
		
		const fxp = require("fast-xml-parser");
		const xml2json = fxp.parse(resultXml);

		const callbackObj = xml2json.xml;
		
		const poServerOrderId = callbackObj.mchnt_order_no;
		const poPayTime = moment().format('YYYY-MM-DD HH:mm:ss');
		
		// 获取订单信息
		const orderInfo = await this.app.mysql.get('pay_order', {poServerOrderId: poServerOrderId});
		if (!orderInfo){
			return false;
		}
		if (orderInfo.poStatus != 0){
			return false;
		}
		
		// 获取商户信息
		const customerInfo = await this.app.mysql.get('pay_customer', {pcId: orderInfo.pcId});

		// 如果主键是自定义的 ID 名称，如 custom_id，则需要在 `where` 里面配置
		const row = {
			poPayTime: poPayTime,
			poStatus: 1
		};
		
		const options = {
		    where: {
		      poServerOrderId: poServerOrderId
		    }
		};
		
		await this.app.mysql.update('pay_order', row, options); // 更新 posts 表中的记录
		// 通知用户数据
		var noticeData = {
			"payKey": customerInfo.pcKey,
			"payMoney": orderInfo.poPrice,
			"userId": orderInfo.poOpenid,
			"customOrderId": orderInfo.poCustomOrderId,
			"orderId": orderInfo.poServerOrderId,
			"customInfo": orderInfo.poCustomInfo        
		};
		// 签名
		const sign = await ctx.service.common.createTkSign(noticeData, customerInfo.pcSignCode);
		noticeData.sign = sign;
		// 记录通知日志
		const pnId = await ctx.service.common.addNotifyLog(customerInfo.pcNoticeUrl, orderInfo.poServerOrderId, noticeData);
		
		// 通知商户
		const noticeResult = await ctx.curl(customerInfo.pcNoticeUrl, {
	        // 必须指定 method
	        method: 'POST',
	        headers: {
				'content-type': 'application/x-www-form-urlencoded',
			},
	        // 通过 contentType 告诉 HttpClient 以 JSON 格式发送
//	        contentType: 'json',
	        data: noticeData,
	        timeout: 9000,
	        // 明确告诉 HttpClient 以 JSON 格式处理返回的响应 body
//	        dataType: 'json',
	    });
	    
	    // 记录通知返回结果
	    ctx.service.common.setNotifyLog(pnId, noticeResult);

		return noticeResult.data;
	}
	
	// 退款  ** 退款id填写不对，会返回验签错误！！！！！！
	async orderRefund(requestObj, customInfo){
		const ctx = this.ctx;

		const poInfo = await ctx.service.order.getInfoFromOrderId(customInfo.pcId, requestObj.customOrderId);
		console.log(poInfo);
		if (!poInfo){
			return '找不到此订单';
		}
		
		if (poInfo.poStatus == 0){
			return '此订单未支付，无法退款';
		}
		if (poInfo.poStatus == 3){
			return '此订单已退款，无法重复退款';
		}
		
		var reqJson = {};
		var pcPayConfig = customInfo.pcPayConfig;
//		pcPayConfig = JSON.parse(pcPayConfig);
		
		reqJson.version = this.app.config.fuyou.parameters.version;
		reqJson.ins_cd = this.app.config.fuyou.parameters.ins_cd;
		reqJson.mchnt_cd = pcPayConfig.fuyou.mchnt_cd;
		reqJson.term_id = this.app.config.fuyou.parameters.term_id;
		reqJson.mchnt_order_no = poInfo.poServerOrderId;
		reqJson.random_str = helper.getUuid();
		reqJson.order_type = 'WECHAT';
		reqJson.refund_order_no = "1432FY" + moment().utc().utcOffset(8).format("YYYYMMDDHHmmss") + "TK" + Math.ceil(Math.random()*10000);
		reqJson.total_amt = poInfo.poPrice;
		reqJson.refund_amt = poInfo.poPrice;
		reqJson.operator_id = '';
//		reqJson.reserved_fy_term_id = '';
//		reqJson.reserved_origi_dt = '';
		console.log('orderRefund req info:');
		console.log(reqJson);
		var sign = await this.createSign(reqJson);
		reqJson.sign = sign;
		
		var reqXml = await this.turnReqXml(reqJson);
		// 退款地址
		var reqUrl = this.app.config.fuyou.reqHost + this.app.config.fuyou.apiOrderRefundUrl;
		reqUrl = reqUrl.replace(/ /, "+");

		// 只用一次gbk，其它方式urlencode结果都不对
		reqXml = urlencode(urlencode(reqXml, 'gbk'));

		var reqData = "req=" + reqXml;
		console.log(reqData)
		// 记录请求日志
		const logId = await ctx.service.common.addReqLog('FY_orderRefund', reqUrl, reqData);
		const result = await this.app.curl(reqUrl, {
			// 必须指定 method
			method: 'POST',
//			// 明确告诉 HttpClient 以 JSON 格式处理返回的响应 body
			dataType: 'text',
//			// 直接发送原始 xml 数据，不需要 HttpClient 做特殊处理
			content: reqData,
			timeout: 9000,
			headers: {
				'content-type': 'application/x-www-form-urlencoded',
			},
		});
		// 记录返回日志
		ctx.service.common.setReqLog(logId, JSON.stringify(result));
		console.log('reqUrl:' + reqUrl);
		console.log(result);

		if (result.status != 200){
			return result.res.statusMessage;
		}

//		console.log(urlencode.decode(result.data, 'gb2312'));
		var resultData = urlencode.decode(result.data, 'gb2312');
		resultData = resultData.replace('<?xml+version="1.0"+encoding="GBK"+standalone="yes"?>', '<?xml version="1.0" encoding="GBK" standalone="yes"?>');

		var xml = resultData;
		const fxp = require("fast-xml-parser");
		const xml2json = fxp.parse(xml);
		console.log('refund xmlToJson:');
		console.log(xml2json);
		const refundInfo = xml2json.xml;
		if (refundInfo.result_code != 0 || refundInfo.result_msg != 'SUCCESS'){
			return '系统错误：' + refundInfo.result_msg;
		}
		return true;
	}
	
	async fyRequestToJson(){
		
		const ctx = this.ctx;
		// 获取post参数
		const requestObj = ctx.request.body;

		var resultXml = urlencode.decode(requestObj.req, 'gb2312');
		
		const fxp = require("fast-xml-parser");
		const xml2json = fxp.parse(resultXml);

		return xml2json.xml;
	}
	
	// 创建签名
	async createSign(reqJson) {
		
		if (reqJson.hasOwnProperty("sign")){
			delete reqJson.sign;
		}
		
		let arr=[];
        for(var key in reqJson){
            arr.push(key)
        }
        arr.sort();
		let str='';
        for(var i in arr){
           str += "&" + arr[i] + "=" + reqJson[arr[i]];
        }

        str = str.substr(1);
        
        var sign = await this.getSign(str);
        return sign;
	}
	
	// 签名字符串
	async getSign(signStr){
		console.log('签名字符串：')
		console.log(signStr)

		var iconv = require('iconv-lite');
		signStr = iconv.encode(signStr, 'GBK');
		
		//获取密匙和公钥
		let privateKey = this.app.config.fuyou.rsaKey.privateKey;
		
		privateKey = helper.formatPKCKey(privateKey, true, false); //转成需要的pem格式。

		const verify = crypto.createSign('md5WithRSAEncryption'); //处理签名
		verify.update(signStr);
//		verify.end();
		const signature = verify.sign(privateKey, 'base64');

		return signature;
	}
	
	// json转为请求xml
	async turnReqXml(requestObj) {
		
		var strXml = '<?xml version="1.0" encoding="GBK" standalone="yes"?><xml>';
		for (var key in requestObj){
			strXml += '<' + key + '>' + requestObj[key] + '</' + key + '>'
		}
		strXml += '</xml>'
		return strXml;
		
		var keyXml = Object.keys(requestObj).sort();			
		for (var i = 0; i < keyXml.length; ++i) {
			let key= keyXml[i];
//			console.log(key)
			if (key != 'undefined' && key != '' && key != null) {
				if (requestObj[key]) {
					strXml += '<' + key + '>' + requestObj[key] + '</' + key + '>'
				} else {
					strXml += '<' + key + '></'+key+'>'
				}
			}
		}
		strXml += '</xml>'
		return strXml;
	}
}

module.exports = FuyouService;